import sys;
from EdgeCtrlPoint import EdgeCtrlPoint;
import EdgeModel, EdgeView;
from PyQt4 import QtCore, QtGui;

class EdgeController(QtGui.QGraphicsObject, object):
    def __init__(self, objFrom=None, objTo=None, pTo=None):
        super(EdgeController, self).__init__();
        self.pointTo = pTo; #To set the CtrlPt & ArrowHead positions
        self.edgeModel = EdgeModel.EdgeModel();
        self.edgeView = EdgeView.EdgeView(self);
        self.objFrom, self.objTo = objFrom, objTo; #Set Source & Target Nodes
        self.isRelinking, self.bRefresh = False, False;
        self.bTargetFound, self.bIsLoop = False, False;
        self.centerTo = None; #To check Inc Moves on New CtrlPts
        self.centerFrom = objFrom.getNodePos_From(); #To check Inc Moves on New CtrlPts
        self.edgeModel.trans = 1;
        self.createEdgePath();

    def getAddCtrlPt_Index(self, newP):
        minDist = sys.maxint;
        nCtrlPts = len(self.gCtrlPt);
        nSteps = 50;
        for i in range(nCtrlPts-1):
            qLineCtrlP = QtCore.QLineF(self.gCtrlPt[i].pos(), self.gCtrlPt[i+1].pos());
            for j in range(nSteps):
                qLineP = QtCore.QLineF(newP, qLineCtrlP.pointAt(float(j)/float(nSteps)));
                nDist = qLineP.length();
                if nDist < minDist:
                    minDist = nDist;
                    iMinPt = i+1;
        if iMinPt > 1:
            return iMinPt;
        return None;
    
    def getRemoveCtrlPt_Index(self, gCtrlPtSelected):
        for i in range(len(self.gCtrlPt)):
            if self.gCtrlPt[i]==gCtrlPtSelected:
                return i;
            
    def addCtrlPt(self, iNewCtrlPt, gNewCtrlPt=None):
        self.gCtrlPt.insert(iNewCtrlPt, gNewCtrlPt);        
        self.nTo += 1;
    
    def removeCtrlPt(self, gCtrlPtSelected):
        self.gCtrlPt.remove(gCtrlPtSelected);
        self.nTo -= 1;
    
    def addEdge(self):
        self.objFrom.addOutEdge(self);
        self.objTo.addInEdge(self);
        
    def removeEdge(self):
        self.objFrom.removeOutEdge(self);
        self.objTo.removeInEdge(self);
        
    def setSource(self, sourceAction):
        self.objFrom = sourceAction;
        
    def setTarget(self, targetNode):
        self.objTo = targetNode;
        self.bTargetFound = True;
        
    def changeSource(self, newSourceAction):
        self.objFrom.removeOutEdge(self);
        newSourceAction.addOutEdge(self);
        self.setSource(newSourceAction);
    
    def changeTarget(self, newTarget):
        self.objTo.removeInEdge(self);
        newTarget.addInEdge(self);
        self.setTarget(newTarget);
        
    def createEdgePath(self):
        #Check if a Target Node is Given to use its Pos (instead of PointTo)
        if self.objTo != None:
            self.bIsLoop = (self.objFrom==self.objTo);
            self.pointTo = self.objTo.getNodePos(self.objFrom.getNodePos_From());
            self.addEdge(); #Register this Edge with both Source & Target Nodes
        #Set all CtrlPt Positions based on Source [& Target if given] Node positions
        self.gCtrlPt = [];
        ptFrom = self.objFrom.getNodePos_From();
        self.gCtrlPt.append(EdgeCtrlPoint(self, ptFrom));
        if self.bIsLoop:
            ptFrom = QtCore.QPointF(ptFrom.x()+100, ptFrom.y()-60);
        self.gCtrlPt.append(EdgeCtrlPoint(self, ptFrom));
        self.gCtrlPt.append(EdgeCtrlPoint(self, self.pointTo));
        self.nFrom, self.nTo = 0, len(self.gCtrlPt)-1;
        self.edgeView.updateEdge();
        
    def boundingRect(self):
        return self.edgeView.getBoundingRect();
    
    def shape(self):
        return self.edgeView.getEdgeRegion();
    
    def checkCtrlPtPosMoved(self):
        bAnyMoved = False;
        for ctrlPt in self.gCtrlPt:
            if ctrlPt.bItemMoved:
                ctrlPt.bItemMoved = False;
                bAnyMoved = True;
        return bAnyMoved;
    
    def moveCtrlPt(self, nCtrlP, newP):
        self.gCtrlPt[nCtrlP].setPos(newP);
    
    def updatePath(self): #Update CtrlPts Position ONLY if Init or Anything Moved
        self.edgeView.refreshTxts();
        fromPos = self.objFrom.getNodePos_From(); 
        #Check if Source, Target or ANY CtrlPt has Moved
        bNewSourcePos = (self.centerFrom!=fromPos);
        bNewTargetPos = (self.objTo!=None and self.centerTo!=self.objTo.pos());
        bNewCtrlPtPos = self.checkCtrlPtPosMoved();
        bAnyMovedPos = (bNewTargetPos or bNewSourcePos or bNewCtrlPtPos);
        self.bAnyMovedPos = bAnyMovedPos;
        #Update All Critical CtrlPts
        #1nd CtrlPt: Keep 1st CtrlPt on Action Source Pos
        self.moveCtrlPt(self.nFrom, fromPos);
        #2nd CtrlPt: If Source has Moved, Update 2nd CtrlPt Incrementally
        if bNewSourcePos and self.objTo!=None:
            dPos = fromPos - self.centerFrom;
            self.centerFrom = fromPos;
            for i in range(self.nFrom+1, self.nTo):
                self.gCtrlPt[i].moveBy(dPos.x(), dPos.y());
        #2nd CtrlPt: Keep same 2nd CtrlPt Absolute pos when looking for Target
        elif self.objTo==None: #2nd CtrlPt: Keep same 2nd CtrlPt Absolute pos when looking for Target
            objPos = self.objFrom.getNodePos_From();
            self.gCtrlPt[self.nFrom+1].setPos(QtCore.QPointF(objPos.x(), objPos.y()));
        #2nd CtrlPt: When Target Found check if is Loop to Update 2nd CtrlPt pos
        elif self.bTargetFound:
            self.bIsLoop = self.objFrom==self.objTo;
            fromPosDir = self.objFrom.getNodePos_From();
            if self.bIsLoop:
                fromPosDir = QtCore.QPointF(fromPosDir.x()+100, fromPosDir.y()-60);
            self.moveCtrlPt(self.nFrom+1, fromPosDir);
            self.bTargetFound = False;
        #Update Last CtrlPt (Target) Pos ONLY when Node is Found and Anything Moved (or just Stop Relinking)
        if (self.objTo!=None) and bAnyMovedPos and (self.isRelinking==False) or self.bRefresh:
            fromPosDir = self.gCtrlPt[self.nTo-1].pos();
            self.moveCtrlPt(self.nTo, self.objTo.getNodePos(fromPosDir));
            self.centerTo = self.objTo.pos();
        #Update Spline and Arrow
        if bAnyMovedPos or self.objTo==None or self.isRelinking or self.bRefresh:
            self.edgeView.updateEdge(); #Update Spline based on the new CtrlPts
            self.bRefresh = False;
    
    def paint(self, painter, option, widget):
        self.prepareGeometryChange();
        self.updatePath();
        return;
        
    def itemChange(self, change, value):
        if change == QtGui.QGraphicsItem.ItemSelectedHasChanged:
            self.edgeView.updateEdgePathItem();
        return QtGui.QGraphicsItem.itemChange(self, change, value);
